﻿using System;
using System.Collections.Generic;
using System.Data.Linq;
using System.Linq;

using Moe.ECamel.Common.Entities;
using Moe.ECamel.Common.Utils;

namespace Moe.ECamel.Server.BL
{
	/// <summary>
	/// Business Logic component for Subscriptions.
	/// </summary>
	public class SubscriptionBL : ECamelBL
	{
		/// <summary>
		/// Default constructor for business logic component.
		/// </summary>
		/// <param name="dataContext">Data context to be used by this component's operations.</param>
		public SubscriptionBL(ECamelDataContext dataContext) 
			: base(dataContext)
		{
		}

		public Subscription GetSubscriptionById(int subscriptionId)
		{
			var adhocDC = CreateDataContextForGetSubscription();

			return (from s in adhocDC.Subscriptions
					where s.SubscriptionId == subscriptionId
					select s).Single();
		}

		public List<Subscription> GetSubscriptionsByCustomer(int customerId)
		{
			var adhocDC = CreateDataContextForGetSubscription();

			return (from s in adhocDC.Subscriptions
					where s.CustomerId == customerId
					select s).ToList();
		}

		private ECamelDataContext CreateDataContextForGetSubscription()
		{
			DataLoadOptions loadOptions = new DataLoadOptions();
			loadOptions.LoadWith<Subscription>(s => s.SubscriptionType);
			loadOptions.LoadWith<SubscriptionType>(s => s.SubscriptionTypeItemTypeInfos);
			loadOptions.LoadWith<Subscription>(s => s.SubscriptionItemTypeInfos);
			loadOptions.LoadWith<SubscriptionItemTypeInfo>(s => s.ItemType);
			ECamelDataContext adhocDC = new ECamelDataContext(dataContext.Connection);
			adhocDC.ObjectTrackingEnabled = false;
			adhocDC.LoadOptions = loadOptions;
			return adhocDC;
		}

		public Subscription GetActiveSubscriptionOfCustomer(int customerId)
		{
			// Configure load options:
			DataLoadOptions loadOptions = new DataLoadOptions();
			loadOptions.LoadWith<Subscription>(s => s.SubscriptionItemTypeInfos);
			ECamelDataContext adhocDC = new ECamelDataContext(dataContext.Connection);
			adhocDC.ObjectTrackingEnabled = false;
			adhocDC.LoadOptions = loadOptions;

			var query = from s in adhocDC.Subscriptions
						where s.CustomerId == customerId && s.Active
						select s;

			return query.SingleOrDefault();
		}

		public ECamelError CreateSubscriptions(IEnumerable<Subscription> subscriptions)
		{
			HashSet<SubscriptionType> subscriptionTypes = new HashSet<SubscriptionType>();

			foreach (var subscription in subscriptions)
			{
				// Validate subscription:
				ECamelError validationError = ValidateSubscription(subscription);
				if (validationError != null)
					return validationError;

				// Add subscription
				dataContext.Subscriptions.InsertOnSubmit(subscription);

				// Attach subscription type if not attached:
				SubscriptionType subscriptionType = subscription.SubscriptionType;
				if (!subscriptionTypes.Contains(subscriptionType))
				{
					dataContext.SubscriptionTypes.Attach(subscriptionType);
					subscriptionTypes.Add(subscriptionType);
				}
			}

			// Return with no error:
			return null;
		}
		public ECamelError UpdateSubscriptionStatus(int subscriptionId, bool active)
		{
			var subscription = (from s in dataContext.Subscriptions
			                    where s.SubscriptionId == subscriptionId
			                    select s).Single();

			// If subscription is being activated, validate no other active subscription:
			if (active)
			{
				var activeSubscription = GetActiveSubscriptionOfCustomer(subscription.CustomerId);
				if (activeSubscription != null && activeSubscription.SubscriptionId != subscriptionId)
					return new ECamelError(MessageIds.SubscriptionInvalidDuplicateActive);

				if (subscription.ExpirationDate < SystemContainer.Clock.Today)
					return new ECamelError(MessageIds.SubscriptionInvalidExpired);
			}

			// Update subscription status:
			subscription.Active = active;
			// Return with no error:
			return null;
		}
		public void DeleteSubscription(int subscriptionId)
		{
			// Find subscription to delete:
			Subscription subscription = (from s in dataContext.Subscriptions
							   where s.SubscriptionId == subscriptionId
							   select s).Single();
			// Delete subscription:
			dataContext.Subscriptions.DeleteOnSubmit(subscription);
			dataContext.SubscriptionItemTypeInfos.DeleteAllOnSubmit(subscription.SubscriptionItemTypeInfos);
		}

		private ECamelError ValidateSubscription(Subscription subscription)
		{
			// Validate subscription:
			ECamelError error = subscription.Validate();
			if (error != null)
				return error;

			// Validate no other active subscriptions:
			var activeSubscription = GetActiveSubscriptionOfCustomer(subscription.CustomerId);
			if (activeSubscription != null && activeSubscription.SubscriptionId != subscription.SubscriptionId)
				return new ECamelError(MessageIds.SubscriptionInvalidDuplicateActive);

			return null;
		}

		public IList<SubscriptionType> GetSubscriptionTypes()
		{
			// Configure load options:
			DataLoadOptions loadOptions = new DataLoadOptions();
			loadOptions.LoadWith<SubscriptionType>(s => s.SubscriptionTypeItemTypeInfos);
			//loadOptions.LoadWith<SubscriptionItemTypeInfo>(s => s.ItemType);
			ECamelDataContext adhocDC = new ECamelDataContext(dataContext.Connection);
			adhocDC.ObjectTrackingEnabled = false;
			adhocDC.LoadOptions = loadOptions;
			// Get subscriptions:
			var query = from st in adhocDC.SubscriptionTypes
						select st;

			return query.ToList();
		}

		/// <summary>
		/// Find active subscriptions which should be expired.
		/// </summary>
		/// <param name="date"></param>
		/// <returns></returns>
		public IList<Subscription> GetExpiredSubscriptions(DateTime date)
		{
			return (from s in dataContext.Subscriptions
			        where s.Active && s.ExpirationDate <= date
			        select s).ToList();
		}
	}
}